﻿#include "Mesh.h"
#include "Algorithm.h"
#include "TopoTables.h"
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ConvertNumComponentsToOffsets.h>

struct FaceTopo
{
  class NumFacesPerCell : public WorkletVisitCellsWithPoints
  {
  public:
    using ControlSignature = void(CellSetIn cell_set, FieldOut numFacesInCell);
    using ExecutionSignature = void(CellShape, _2);

    template<typename CellShapeTag>
    VTKM_EXEC void operator()(CellShapeTag shape, IdComponent& num_faces) const
    {
      CellFaceNumberOfFaces(shape, num_faces);
    }
  };

  struct BuildDuplicateFace : public WorkletVisitCellsWithPoints
  {
    using ControlSignature = void(CellSetIn cellset, FieldOut face_id, FieldOut face_info);
    using ExecutionSignature = void(CellShape, PointIndices, InputIndex, VisitIndex, _2, _3);
    using ScatterType = ScatterCounting;

    template<typename CellShapeTag, typename C2NType, typename FaceInfo>
    VTKM_EXEC void operator()(CellShapeTag cell_shape,
                              const C2NType& c2n,
                              Id inputIndex,
                              IdComponent visitIndex,
                              Id3& faceId,
                              FaceInfo& face) const
    {
      CellFaceCanonicalId(visitIndex, cell_shape, c2n, faceId);
      face = { inputIndex, static_cast<UInt8>(visitIndex) };
    }
  };

  struct BuildUniqueFace : public WorkletReduceByKey
  {
    using ControlSignature = void(KeysIn faceKey,
                                  ValuesIn faceInfo,
                                  //ReducedValuesOut face_link,
                                  WholeArrayOut cell_links);
    using ExecutionSignature = void(_2,/* _3,*/ _3);

    template<typename FaceInfoPair, typename CellToCellType/*, typename FaceInfo*/>
    VTKM_EXEC void operator()(const FaceInfoPair& face_info,
                              //Vec<FaceInfo, 2>& face_link,
                              CellToCellType& cell_link) const
    {
      if (face_info.GetNumberOfComponents() == 2) // 内部面
      {
        //face_link[0] = face_info[0];
        //face_link[1] = face_info[1];

        auto cell_id = face_info[0].Get().first;
        auto face_id = face_info[0].Get().second;
        cell_link.Get(cell_id)[face_id] = face_info[1];

        cell_id = face_info[1].Get().first;
        face_id = face_info[1].Get().second;
        cell_link.Get(cell_id)[face_id] = face_info[0];
      }
      else if (face_info.GetNumberOfComponents() == 1) // 边界面
      {
        //face_link[0] = face_info[0];
        // TODO: 引入边界条件
        //face_link[1] = { -1, 0 };

        auto cell_id = face_info[0].Get().first;
        auto face_id = face_info[0].Get().second;
        cell_link.Get(cell_id)[face_id] = {-1,0};
      }
      else
      {
        this->RaiseError("face_duplicate.GetNumberOfComponents error.");
      }
    }
  };
};

void Mesh::BuildFaceTopo()
{
  ArrayHandle<IdComponent> num_face_per_cell;
  Invoker{}(FaceTopo::NumFacesPerCell{}, _cell_set, num_face_per_cell);

  ArrayHandle<Id3> face_id;
  ArrayHandle<FaceInfo> face_info; //包含重复face
  Invoker{}(FaceTopo::BuildDuplicateFace{},
            ScatterCounting{ num_face_per_cell },
            _cell_set,
            face_id,
            face_info);


  _cell_links.Allocate(face_info.GetNumberOfValues());
  _face_num_offset = vtkm::cont::ConvertNumComponentsToOffsets(num_face_per_cell);
  auto cell_links = vtkm::cont::make_ArrayHandleGroupVecVariable(_cell_links, _face_num_offset);
  Keys<Id3> face_keys(face_id);
  Invoker{}(FaceTopo::BuildUniqueFace{}, face_keys, face_info, /*_face_links,*/ cell_links);
}
